home *** CD-ROM | disk | FTP | other *** search
/ Chip 2007 January, February, March & April / Chip-Cover-CD-2007-02.iso / Pakiet bezpieczenstwa / mini Pentoo LiveCD 2006.1 / mpentoo-2006.1.iso / livecd.squashfs / usr / lib / python2.4 / idlelib / CodeContext.py < prev    next >
Text File  |  2005-10-18  |  6KB  |  140 lines

  1. """CodeContext - Display the block context of code at top of edit window
  2.  
  3. Once code has scrolled off the top of the screen, it can be difficult
  4. to determine which block you are in.  This extension implements a pane
  5. at the top of each IDLE edit window which provides block structure
  6. hints.  These hints are the lines which contain the block opening
  7. keywords, e.g. 'if', for the enclosing block.  The number of hint lines
  8. is determined by the numlines variable in the CodeContext section of
  9. config-extensions.def. Lines which do not open blocks are not shown in
  10. the context hints pane.
  11.  
  12. """
  13. import Tkinter
  14. from configHandler import idleConf
  15. from sets import Set
  16. import re
  17.  
  18. BLOCKOPENERS = Set(["class", "def", "elif", "else", "except", "finally", "for",
  19.                     "if", "try", "while"])
  20. INFINITY = 1 << 30
  21. UPDATEINTERVAL = 100 # millisec
  22. FONTUPDATEINTERVAL = 1000 # millisec
  23.  
  24. getspacesfirstword = lambda s, c=re.compile(r"^(\s*)(\w*)"): c.match(s).groups()
  25.  
  26. class CodeContext:
  27.     menudefs = [('options', [('!Code Conte_xt', '<<toggle-code-context>>')])]
  28.  
  29.     numlines = idleConf.GetOption("extensions", "CodeContext",
  30.                                   "numlines", type="int", default=3)
  31.     bgcolor = idleConf.GetOption("extensions", "CodeContext",
  32.                                  "bgcolor", type="str", default="LightGray")
  33.     fgcolor = idleConf.GetOption("extensions", "CodeContext",
  34.                                  "fgcolor", type="str", default="Black")
  35.     def __init__(self, editwin):
  36.         self.editwin = editwin
  37.         self.text = editwin.text
  38.         self.textfont = self.text["font"]
  39.         self.label = None
  40.         # Dummy line, which starts the "block" of the whole document:
  41.         self.info = list(self.interesting_lines(1))
  42.         self.lastfirstline = 1
  43.         visible = idleConf.GetOption("extensions", "CodeContext",
  44.                                      "visible", type="bool", default=False)
  45.         if visible:
  46.             self.toggle_code_context_event()
  47.             self.editwin.setvar('<<toggle-code-context>>', True)
  48.         # Start two update cycles, one for context lines, one for font changes.
  49.         self.text.after(UPDATEINTERVAL, self.timer_event)
  50.         self.text.after(FONTUPDATEINTERVAL, self.font_timer_event)
  51.  
  52.     def toggle_code_context_event(self, event=None):
  53.         if not self.label:
  54.             self.label = Tkinter.Label(self.editwin.top,
  55.                                       text="\n" * (self.numlines - 1),
  56.                                       anchor="w", justify="left",
  57.                                       font=self.textfont,
  58.                                       bg=self.bgcolor, fg=self.fgcolor,
  59.                                       relief="sunken",
  60.                                       width=1, # Don't request more than we get
  61.                                       )
  62.             self.label.pack(side="top", fill="x", expand=0,
  63.                             after=self.editwin.status_bar)
  64.         else:
  65.             self.label.destroy()
  66.             self.label = None
  67.         idleConf.SetOption("extensions", "CodeContext", "visible",
  68.                            str(self.label is not None))
  69.         idleConf.SaveUserCfgFiles()
  70.  
  71.     def get_line_info(self, linenum):
  72.         """Get the line indent value, text, and any block start keyword
  73.  
  74.         If the line does not start a block, the keyword value is False.
  75.         The indentation of empty lines (or comment lines) is INFINITY.
  76.         There is a dummy block start, with indentation -1 and text "".
  77.  
  78.         Return the indent level, text (including leading whitespace),
  79.         and the block opening keyword.
  80.  
  81.         """
  82.         if linenum == 0:
  83.             return -1, "", True
  84.         text = self.text.get("%d.0" % linenum, "%d.end" % linenum)
  85.         spaces, firstword = getspacesfirstword(text)
  86.         opener = firstword in BLOCKOPENERS and firstword
  87.         if len(text) == len(spaces) or text[len(spaces)] == '#':
  88.             indent = INFINITY
  89.         else:
  90.             indent = len(spaces)
  91.         return indent, text, opener
  92.  
  93.     def interesting_lines(self, firstline):
  94.         """Generator which yields context lines, starting at firstline."""
  95.         # The indentation level we are currently in:
  96.         lastindent = INFINITY
  97.         # For a line to be interesting, it must begin with a block opening
  98.         # keyword, and have less indentation than lastindent.
  99.         for line_index in xrange(firstline, -1, -1):
  100.             indent, text, opener = self.get_line_info(line_index)
  101.             if indent < lastindent:
  102.                 lastindent = indent
  103.                 if opener in ("else", "elif"):
  104.                     # We also show the if statement
  105.                     lastindent += 1
  106.                 if opener and line_index < firstline:
  107.                     yield line_index, text
  108.  
  109.     def update_label(self):
  110.         firstline = int(self.text.index("@0,0").split('.')[0])
  111.         if self.lastfirstline == firstline:
  112.             return
  113.         self.lastfirstline = firstline
  114.         tmpstack = []
  115.         for line_index, text in self.interesting_lines(firstline):
  116.             # Remove irrelevant self.info items, and when we reach a relevant
  117.             # item (which must happen because of the dummy element), break.
  118.             while self.info[-1][0] > line_index:
  119.                 del self.info[-1]
  120.             if self.info[-1][0] == line_index:
  121.                 break
  122.             tmpstack.append((line_index, text))
  123.         while tmpstack:
  124.             self.info.append(tmpstack.pop())
  125.         lines = [""] * max(0, self.numlines - len(self.info)) + \
  126.                 [x[1] for x in self.info[-self.numlines:]]
  127.         self.label["text"] = '\n'.join(lines)
  128.  
  129.     def timer_event(self):
  130.         if self.label:
  131.             self.update_label()
  132.         self.text.after(UPDATEINTERVAL, self.timer_event)
  133.  
  134.     def font_timer_event(self):
  135.         newtextfont = self.text["font"]
  136.         if self.label and newtextfont != self.textfont:
  137.             self.textfont = newtextfont
  138.             self.label["font"] = self.textfont
  139.         self.text.after(FONTUPDATEINTERVAL, self.font_timer_event)
  140.